/*
 * Copyright (C) 2023, KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

#include "sidebar-view.h"
#include "sidebar-window-helper.h"
#include "screen-monitor.h"
#include "global-settings.h"
//#include "layout-config.h"
#include "window-blur-helper.h"
#include "notification-window.h"

#include <QUrl>
#include <QQmlEngine>
#include <QQmlContext>
#include <QGuiApplication>
#include <QQuickItem>
#include <QX11Info>

// 两种模式下侧边栏按钮区域的宽度
#define PC_SIDEBAR_WIDTH     384
#define TABLET_SIDEBAR_WIDTH 540

using namespace Sidebar;

SidebarView::SidebarView(QWindow *parent) : SharedEngineView(parent)
{
    setColor(Qt::transparent);
    setResizeMode(SharedEngineView::SizeRootObjectToView);
    setFlags(Qt::FramelessWindowHint | Qt::Window);

    m_windowBlurHelper = new WindowBlurHelper(this);
    m_windowBlurHelper->setWindow(this);
    connect(m_windowBlurHelper, &WindowBlurHelper::radiusChanged, this, [this] {
        // * wm...
        update();
    });

    // 3.窗口尺寸变化,屏幕变化
    setScreen(ScreenMonitor::getInstance()->getPrimaryScreen());
    connect(ScreenMonitor::getInstance(), &ScreenMonitor::primaryScreenChanged, this, [this] {
        activeWindow(false);
        setScreen(ScreenMonitor::getInstance()->getPrimaryScreen());
        updateWindowGeometry();
    });
    connect(ScreenMonitor::getInstance(), &ScreenMonitor::geometryChanged, this, &SidebarView::updateWindowGeometry);
    connect(ScreenMonitor::getInstance(), &ScreenMonitor::panelPropertyChanged, this, &SidebarView::updateWindowGeometry);

    setIsNotificationEmpty(UkuiNotification::NotificationCenterWindow::globalGroupModel()->rowCount(QModelIndex()) == 0);
    connect(UkuiNotification::NotificationCenterWindow::globalGroupModel(), &UkuiNotification::NotificationGroupModel::rowsInserted,
            this, [this] {
        setIsNotificationEmpty(UkuiNotification::NotificationCenterWindow::globalGroupModel()->rowCount(QModelIndex()) == 0);
    });
    connect(UkuiNotification::NotificationCenterWindow::globalGroupModel(), &UkuiNotification::NotificationGroupModel::rowsRemoved,
            this, [this] {
        setIsNotificationEmpty(UkuiNotification::NotificationCenterWindow::globalGroupModel()->rowCount(QModelIndex()) == 0);
    });

    // 2.平板模式切换
    onSystemModeChanged();
    updateWindowProp();
    m_radius = GlobalSettings::globalInstance()->getValue(UKUI_STYLE_WINDOW_RADIUS_KEY).toInt();
    connect(GlobalSettings::globalInstance(), &GlobalSettings::valueChanged, this, [this] (const QString &key) {
        if (key == TABLET_MODE) {
            onSystemModeChanged();
        } else if (key == UKUI_STYLE_WINDOW_RADIUS_KEY) {
            m_radius = GlobalSettings::globalInstance()->getValue(key).toInt();
            Q_EMIT radiusChanged();
        }
    });

    connect(this, &QQuickView::activeFocusItemChanged, this, [this] {
        if (!activeFocusItem()) {
            activeWindow(false);
        }
    });

    rootContext()->setContextProperty("sidebarWindow", this);
    rootContext()->setContextProperty("windowBlurHelper", m_windowBlurHelper);
}

void SidebarView::init()
{
    // init
    engine()->addImportPath("qrc:/qml");
    setSource(QUrl("qrc:/qml/Sidebar.qml"));
}

void SidebarView::activeWindow(bool active)
{
    if (!rootObject()) {
        return;
    }

    // 获取是否存在执行中的动画
    QVariant isRunning(false);
    QMetaObject::invokeMethod(rootObject(), "isRunning", Qt::DirectConnection, Q_RETURN_ARG(QVariant, isRunning), Q_ARG(QVariant, active));

    if (isRunning.toBool() || (active == isVisible())) {
        return;
    }

    // 初始化动画属性
    QMetaObject::invokeMethod(rootObject(), "updateProperty", Qt::DirectConnection, Q_ARG(QVariant, active));

    if (active) {
        show();
        Q_EMIT requestShowContent();

    } else {
        Q_EMIT requestHideContent();
    }
}

void SidebarView::onSystemModeChanged()
{
    bool isTabletMode = GlobalSettings::globalInstance()->getValue(TABLET_MODE).toBool();
    if (isTabletMode == m_isTabletMode) {
        return;
    }

    m_isTabletMode = isTabletMode;

    // TODO: 平板切换隐藏动画
//    activeWindow(false);
    setWindowVisible(false);

    //LayoutConfig::getInstance()->updateLayout(m_isTabletMode);
    updateWindowGeometry();

    Q_EMIT isTabletModeChanged();
}

bool SidebarView::isTabletMode()
{
    return m_isTabletMode;
}

void SidebarView::updateWindowGeometry()
{
    ScreenMonitor *screenMonitor = ScreenMonitor::getInstance();
    QRect screenRect = screenMonitor->getGeometry();

    // 8%的屏幕宽度为最小滑动距离
    m_minimumThreshold   = static_cast<qint32>(screenRect.width() * 0.05);
    m_primaryScreenRight = screenRect.right();

    updateGeometryOfWindow();
    updateGeometryOfMask();

    SidebarWindowHelper::setWindowGeometry(this, m_windowGeometry);
    setMask(m_maskGeometry);

    m_windowBlurHelper->setRegion(m_padding, m_windowGeometry.height() - m_blurHeight - m_padding, m_windowGeometry.width() - m_padding*2, m_blurHeight, radius());

    if (rootObject()) {
        rootObject()->setSize(m_windowGeometry.size());
    }

    Q_EMIT minimumThresholdChanged();
    Q_EMIT primaryScreenRightChanged();
}

qint32 SidebarView::windowPadding() const
{
    return m_padding;
}

qint32 SidebarView::minimumThreshold() const
{
    return m_minimumThreshold;
}

qint32 SidebarView::primaryScreenRight() const
{
    return m_primaryScreenRight;
}

qint32 SidebarView::minWindowSize() const
{
    return m_minWindowSize - m_padding*2;
}

void SidebarView::setBlurHeight(int height)
{
    m_windowBlurHelper->setRegion(m_padding, m_windowGeometry.height() - height - m_padding, PC_SIDEBAR_WIDTH , height, radius());
    m_blurHeight = height;
}

qint32 SidebarView::radius() const
{
    bool isOpenGLEnv = rootContext()->contextProperty("isOpenGLEnv").toBool();
    return isOpenGLEnv ? m_radius : 0;
}

void SidebarView::updateWindowProp()
{
    SidebarWindowHelper::setWindowFlags(this, SidebarWindowType::Sidebar);
    SidebarWindowHelper::setWindowAttribute(this, SidebarWindowType::Sidebar);
    updateWindowGeometry();
}

void SidebarView::updateGeometryOfWindow()
{
    ScreenMonitor *screenMonitor = ScreenMonitor::getInstance();
    QRect screenRect = screenMonitor->getGeometry();
    int width = PC_SIDEBAR_WIDTH + m_padding * 2;
    int panelSize = screenMonitor->getPanelSize();
    int panelPosition = screenMonitor->getPanelPosition();

    // 任务栏位置 上: 1, 下: 0, 左: 2, 右: 3
    switch(panelPosition) {
        default:
        case 0:
            m_windowGeometry = QRect(screenRect.right() - width,
                                     screenRect.y(),
                                     width,
                                     (screenRect.height() - panelSize));
            break;
        case 1:
            m_windowGeometry = QRect(screenRect.right() - width,
                                     screenRect.y() + panelSize,
                                     width,
                                     (screenRect.height() - panelSize));
            break;
        case 2:
            m_windowGeometry = QRect(screenRect.right() - width,
                                     screenRect.y(),
                                     width,
                                     screenRect.height());
            break;
        case 3:
            m_windowGeometry = QRect(screenRect.right() - width - panelSize,
                                     screenRect.y(),
                                     width,
                                     screenRect.height());
            break;
    }
}

void SidebarView::updateGeometryOfMask()
{
    m_maskGeometry = QRect(0,
                           m_isNotificationEmpty ? (m_windowGeometry.height() - m_minWindowSize) : 0,
                           m_windowGeometry.width(),
                           m_isNotificationEmpty ? m_minWindowSize : m_windowGeometry.height());
}


void SidebarView::focusInEvent(QFocusEvent *event)
{
    QQuickWindow::focusInEvent(event);
}

void SidebarView::focusOutEvent(QFocusEvent *event)
{
    QQuickWindow::focusOutEvent(event);
}

void SidebarView::resizeEvent(QResizeEvent *event)
{
    SharedEngineView::resizeEvent(event);
}

bool SidebarView::event(QEvent *event)
{
    if (event->type() == QEvent::Move) {
        updateWindowGeometry();
        return true;
    }
    return QQuickWindow::event(event);
}

void SidebarView::exposeEvent(QExposeEvent *event)
{
    if (isExposed()) {
        updateWindowProp();
        if (QX11Info::isPlatformX11()) {
            requestActivate();
        }
    }
    QQuickWindow::exposeEvent(event);
}

bool SidebarView::contentVisible() const
{
    return m_contentVisible;
}

void SidebarView::setContentVisible(bool visible)
{
    m_contentVisible = visible;
    Q_EMIT contentVisibleChanged();
}

bool SidebarView::isNotificationEmpty() const
{
    return m_isNotificationEmpty;
}

void SidebarView::setIsNotificationEmpty(bool isEmpty)
{
    if (m_isNotificationEmpty == isEmpty) return;
    m_isNotificationEmpty = isEmpty;
    Q_EMIT isNotificationEmptyChanged();
}

bool SidebarView::isWindowFold() const
{
    return m_isWindowFold;
}

void SidebarView::setIsWindowFold(bool isFold)
{
    if (m_isWindowFold == isFold) return;
    m_isWindowFold = isFold;

    updateGeometryOfMask();
    setMask(m_maskGeometry);

    Q_EMIT isWindowFoldChanged();
}

void SidebarView::setWindowVisible(bool visible)
{
    setVisible(visible);
    if (!visible) {
        setContentVisible(visible);
    }
}
